/**
 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
 * This file is part of CSipSimple.
 *
 *  CSipSimple is free software: you can redistribute it and/or modify
 *  it under thde terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  If you own a pjsip commercial license you can also redistribute it
 *  and/or modify it under the terms of the GNU Lesser General Public License
 *  as an android library.
 *
 *  CSipSimple is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with CSipSimple.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.cloudptt.api.product.pjsua2.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.view.Surface;
import android.view.SurfaceView;
import android.widget.Toast;

import androidx.core.app.NotificationCompat;

import com.cloudptt.api.comm.CauseCont;
import com.cloudptt.api.pjsip.api.ImsProfile;
import com.cloudptt.api.pjsip.api.MessageInfo;
import com.cloudptt.api.pjsip.api.SipCallSession;
import com.cloudptt.api.pjsip.api.SipManager;
import com.cloudptt.api.pjsip.service.SipWakeLock;
import com.cloudptt.api.product.pjsua2.MyApp;
import com.cloudptt.api.product.pjsua2.MyCall;
import com.cloudptt.api.pjsua2.api.IPjsuaService;
import com.cloudptt.api.product.func.call.CallAttr;
import com.cloudptt.api.product.log.Log;
import com.hytera.productsdk.R;
import com.mcptt.config.VideoConfig;

import org.pjsip.pjsua2.pjsip_status_code;

import java.lang.ref.WeakReference;
import java.util.concurrent.Semaphore;

public class PjsuaService extends Service {

	// static boolean creating = false;
	private static final String THIS_FILE = "PjsuaService";

	private SipWakeLock sipWakeLock;
	//	public static MyAccount account = null;
	private static MyApp app = null;
	private static PjsuaService singlePjsuaService;
	private static final String CHANNEL_ID = "PjsuaServiceChannel";


	@Override
	public void onCreate()
	{
		super.onCreate();
		Log.d(THIS_FILE, "SipService onCreate");
		Log.setLogLevel(6);
		singlePjsuaService = this;
		sipWakeLock = new SipWakeLock((PowerManager) getSystemService(Context.POWER_SERVICE));
	}

	/**
	 * {@inheritDoc}
	 */
	@SuppressWarnings("deprecation")
	@Override
	public void onStart(Intent intent, int startId)
	{
		super.onStart(intent, startId);
		Log.d(THIS_FILE, "SipService onStart");
		//String svrIp = intent.getStringExtra("svr_ip");
		//int tcpPort = intent.getIntExtra("tcp_port",0);
		//Log.d(THIS_FILE, "SipService onStart svrIp = " + svrIp + " tcpPort = " + tcpPort);
//		getExecutor().execute(new StartRunnable());

	}

	@Override
	public IBinder onBind(Intent intent)
	{
		Log.d(THIS_FILE, "SipService onBind");
		String who = intent.getStringExtra(SipManager.EXTERA);
		Log.d(THIS_FILE, "onBind who is " + who);
		String serviceName = intent.getAction();
		Log.d(THIS_FILE, "Action is " + serviceName);
//		if (serviceName == null
//				|| serviceName.equalsIgnoreCase(SipManager.INTENT_SIP_SERVICE))
//		{
//			Log.d(THIS_FILE, "Service returned");
//			return binder;
//		}
		Log.d(THIS_FILE, "SipService onBind startForeground begin");
		createNotificationChannel();
		Notification notification = buildNotification();
		startForeground(1, notification);

		Log.d(THIS_FILE, "SipService onBind StartRunnable begin");
		getExecutor().execute(new StartRunnable());
		Log.d(THIS_FILE, "SipService onBind end");

		Log.d(THIS_FILE, "Default service (SipService) returned");
		return binder;
	}
	private void createNotificationChannel() {
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			NotificationChannel channel = new NotificationChannel(
					CHANNEL_ID,
					"Foreground Service Channel",
					NotificationManager.IMPORTANCE_LOW
			);
			getSystemService(NotificationManager.class).createNotificationChannel(channel);
		}
	}
	@Override
	public boolean onUnbind(Intent intent) {
		// TODO Auto-generated method stub
		Log.d(THIS_FILE, "SipService onUnbind");
		return super.onUnbind(intent);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// 创建通知并启动前台服务
		Log.d(THIS_FILE, "SipService onStartCommand");


		return START_STICKY;
	}

	private Notification buildNotification() {
		// 适配 Android 8.0+ 的通知渠道
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
			NotificationChannel channel = new NotificationChannel(
					"channel_id", "Foreground Service",
					NotificationManager.IMPORTANCE_LOW
			);
			getSystemService(NotificationManager.class).createNotificationChannel(channel);
		}

		return new NotificationCompat.Builder(this, "channel_id")
				.setContentTitle("服务运行中")
				.setContentText("正在执行任务")
				.setSmallIcon(R.drawable.ptticon)
				.build();
	}
	@Override
	public void onLowMemory() {
		// TODO Auto-generated method stub
		super.onLowMemory();
		Log.d(THIS_FILE, "onLowMemory");
	}

	@Override
	public void onTrimMemory(int level) {
		// TODO Auto-generated method stub
		super.onTrimMemory(level);
		Log.d(THIS_FILE, "onTrimMemory level = " + level);
	}


	@Override
	public void onDestroy()
	{
		super.onDestroy();
		Log.d(THIS_FILE, "SipService onDestroy");
		singlePjsuaService = null;
//		unregisterServiceBroadcasts();
//		notificationManager.onServiceDestroy();
		getExecutor().execute(new FinalizeDestroyRunnable());
	}


	// Implement public interface for the service
	private final IPjsuaService.Stub binder = new IPjsuaService.Stub() {

		@Override
		public void sipStart() throws RemoteException
		{
			Log.d(THIS_FILE, "sipStart");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new StartRunnable());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void sipStop() throws RemoteException
		{
			Log.d(THIS_FILE, "sipStop");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new StopRunnable());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void forceStopService() throws RemoteException
		{
			Log.d(THIS_FILE, "forceStopService");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			//			stopHandShakeTimer();
			//			stopNegotiateTimer();
			//			
			//			unregisterBroadcasts();
			//			unregisterServiceBroadcasts();
			//			notificationManager.onServiceDestroy();		
			//			sipWakeLock.reset();
			//			getExecutor().execute(new FinalizeDestroyRunnable());
			getExecutor().execute(new DestroyRunnable());
			//			getExecutor().execute(new FinalizeDestroyRunnable());
			stopSelf();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void askThreadedRestart() throws RemoteException
		{
			Log.d(THIS_FILE, "askThreadedRestart");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new RestartRunnable());
		};

		@Override
		public void addAccount(ImsProfile imsProfile, String pwd) throws RemoteException
		{
			Log.d(THIS_FILE, "addAccount imsProfile = " + imsProfile);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			Log.d(THIS_FILE, "addAccount");
			getExecutor().execute(new SipRunnable() {
				@Override
				public void doRun() throws SameThreadException {
					PjsuaService.this.addAccount(imsProfile,pwd);

				}
			});
		}

		@Override
		public void refreshAccount() throws RemoteException
		{
			Log.d(THIS_FILE, "refreshAccount");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			Log.d(THIS_FILE, "addAccount");
			getExecutor().execute(new SipRunnable() {
				@Override
				public void doRun() throws SameThreadException {
					PjsuaService.this.refreshCurAcc();

				}
			});
		}

		@Override
		public void setVideoAttr(final int videoResolving) throws RemoteException
		{
			Log.d(THIS_FILE, "setVideoAttr videoResolving = " + VideoConfig.getVideoResolvingDesc(videoResolving));
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new SipRunnable() {
				@Override
				public void doRun() throws SameThreadException {
//					pjService.setVideoAttr(videoResolving);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void removeAllAccounts() throws RemoteException
		{
			Log.d(THIS_FILE, "removeAllAccounts");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new SipRunnable() {
				@Override
				public void doRun() throws SameThreadException {
					PjsuaService.this.unregisterAllAccounts(true);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void delCurAccount() throws RemoteException
		{
			Log.d(THIS_FILE, "delCurAccounts");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new SipRunnable() {
				@Override
				public void doRun() throws SameThreadException {
					PjsuaService.this.delCurAcc();
				}
			});
		}

		@Override
		public int sendPublishPocSetting(int mcType,String accessToken) throws RemoteException
		{
			Log.d(THIS_FILE, "sendPublishPocSetting mcType = " + mcType);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().sndPublishPocSetting(mcType,accessToken);
					}catch (Exception e)
					{
						e.printStackTrace();
					}
					return CauseCont.OperateCheck_Suc;
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendPublishFuncLogx(boolean isActive,String uuId,String funcNum,boolean isForce,String initiateId) throws RemoteException
		{
			Log.d(THIS_FILE, "sendFuncLogx isActive = " + isActive +  " uuId = " + uuId + " funcNum = " + funcNum + " isForce = " + isForce + " initiateId = " + initiateId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().sendPublishFuncLogx(isActive,uuId,funcNum,isForce,initiateId);
					}catch (Exception e)
					{
						e.printStackTrace();
					}
					return CauseCont.OperateCheck_Suc;
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendPublishFuncLogoutAll(String[] funcNumList) throws RemoteException
		{
			Log.d(THIS_FILE, "sendPublishFuncLogx funcNumList = " + funcNumList);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().sendPublishFuncLogoutAll(funcNumList);
					}catch (Exception e)
					{
						e.printStackTrace();
					}
					return CauseCont.OperateCheck_Suc;
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int subcribeFuncSelf() throws RemoteException
		{
			Log.d(THIS_FILE, "subcribeFuncSelf");

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().subcribeFuncSelf();
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int subcribeFunc(String uuId,String funcNum) throws RemoteException
		{
			Log.d(THIS_FILE, "subcribeFunc uuId = " + uuId + " funcNum = " + funcNum);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().subcribeFunc(uuId,funcNum);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int subcribeCms(int mcType,String accessToken) throws RemoteException
		{
			Log.d(THIS_FILE, "subcribeCmsGms");

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().subcribeCms(mcType,accessToken);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int subcribeGms(String[] groupIdArray,String accessToken) throws RemoteException
		{
			Log.d(THIS_FILE, "subcribeGms groupIdArray = " + groupIdArray);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().subcribeGms(groupIdArray,accessToken);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendFdFindMessage() throws RemoteException
		{
			Log.d(THIS_FILE, "sendFdFindMessage");

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().sendFdFindMessage();
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendSdsMessage(int type,final MessageInfo msgInfo, String peerMcdataId) throws RemoteException
		{
			Log.d(THIS_FILE, "sendSdsMessage msgInfo = " + msgInfo + " peerMcdataId = " + peerMcdataId);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().sendSdsMessage(type,msgInfo,peerMcdataId);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendFdMessage(int type,final MessageInfo msgInfo, String peerMcdataId) throws RemoteException
		{
			Log.d(THIS_FILE, "sendFdMessage msgInfo = " + msgInfo + " peerMcdataId = " + peerMcdataId);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().sendFdMessage(type,msgInfo,peerMcdataId);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}
		@Override
		public int sendNtfMessage(final MessageInfo msgInfo, String peerMcdataId) throws RemoteException
		{
			Log.d(THIS_FILE, "sendNtfMessage msgInfo = " + msgInfo + " peerMcdataId = " + peerMcdataId);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().sendNtfMessage(msgInfo,peerMcdataId);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int sendMessage(final int sessionType,final int msgType,final long len,final String message, final String recver) throws RemoteException
		{
			Log.d(THIS_FILE, "sendMessage msgType = " + msgType + " len = " + len + " message = " + message + " recver = " + recver);

			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
//					return app.getCurAcc().sendMessage(sessionType,msgType,len,message,recver);
					return CauseCont.OperateCheck_Suc;
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendCmdMessage(String peerMcpttId,String cmdType) throws RemoteException
		{
			Log.d(THIS_FILE, "sendSysMsg peerMcpttId = " + peerMcpttId + " cmdType = " + cmdType);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
//					return app.getCurAcc().sendMessage(sessionType,msgType,len,message,recver);
					return app.getCurAcc().sendCmdMessage(peerMcpttId,cmdType);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}
		/**
		 * {@inheritDoc}
		 */
		@Override
		public int sendLocationMessage(String locationInfoStr) throws RemoteException
		{
//			Log.d(THIS_FILE, "sendLocalMessage locationInfoStr = " + locationInfoStr);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

//			final String num = acc.rewriteNumber(callee);
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
//					return app.getCurAcc().sendMessage(sessionType,msgType,len,message,recver);
					return app.getCurAcc().sendLocationMessage(locationInfoStr);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}
		/**
		 * {@inheritDoc}
		 */
		@Override
		public int sendSystemMessage(final String msgType) throws RemoteException
		{
			Log.d(THIS_FILE, "sendSystemMessage msgType = " + msgType);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			SipService.this.startService(new Intent(SipService.this, SipService.class));

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}
//			final String sysNum = acc.rewriteNumber("0000000000");
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().sendSysMessage(msgType);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendSubscribe(final String uri) throws RemoteException
		{
			Log.d(THIS_FILE, "sendSubscribe uri = " + uri);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return 0;//pjService.sendSubscribe(uri);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int sendPublish(final String  groupNum) throws RemoteException
		{
			Log.d(THIS_FILE, "sendPublish groupNum = " + groupNum);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}
			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return 0;//account.sendPublish(groupNum);
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int makeCallWithOptions(final String callee,final int callType,final boolean isHaveVideo,final boolean isEmergency,String funcNum,String locationStr) throws RemoteException
		{
			Log.d(THIS_FILE, "makeCallWithOptions callee is " + callee + " callType is" + callType + " isHaveVideo = " + isHaveVideo + " isEmergency = " + isEmergency + " funcNum = " + funcNum);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			//We have to ensure service is properly started and not just binded
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				return checkResult;
			}

			checkResult = app.checkCall(callee);
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					String key = app.getCurAcc().getNewCallIdStr(callType, CallAttr.CallDir_MO);
					MyCall call = app.getCurAcc().getMyCall(key);
					try {
						return call.makeCall(callee, callType, isHaveVideo,isEmergency,funcNum,locationStr);
					}
					catch (Exception e)
					{
						call.delete();
						call = null;
						app.getCurAcc().delMyCall(key);
						return CauseCont.OperateErr_Exception;
					}
				}
			};

			getExecutor().execute(action);
			Integer result = (Integer) action.getResult();
			if(result.intValue() == 0)
			{
				return CauseCont.OperateCheck_Suc;
			}
			else
			{
				return CauseCont.OperateErr_Exception;
			}
		}

		@Override
		public int answer(final String callIdStr,final int status, final boolean hasVideo) throws RemoteException
		{
			Log.d(THIS_FILE, "answer callIdStr = " + callIdStr + " status = " + status +  " hasVideo = " + hasVideo);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

//			final MyAccount acc = getAccount();
//			Log.e(THIS_FILE, "answer acc.acc_id is "  + acc.getId());
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().getMyCall(callIdStr).answer(status,hasVideo);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int hangup(final String callIdStr, final int status) throws RemoteException
		{
			Log.d(THIS_FILE, "hangup callIdStr is " + callIdStr + " status is " + status );
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().getMyCall(callIdStr).hangup(status);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int subcribeConference(final String callIdStr, final boolean isActive, final String sessionId,final String groupNum,boolean hasVideo) throws RemoteException
		{
			Log.d(THIS_FILE, "subcribeConference callIdStr = " + callIdStr + " isActive = " + isActive + " sessionId = " + sessionId + " groupNum = " + groupNum + " hasVideo = " + hasVideo);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}


			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return app.getCurAcc().getMyCall(callIdStr).subcribeConference(isActive,sessionId,groupNum,hasVideo);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public void clearAllCalls()
		{
			Log.d(THIS_FILE, "clearAllCalls");
			removeAllSipCallSession();
		}

		@Override
		public void clearResourceForCallEnd(String callIdStr)
		{
			Log.d(THIS_FILE, "clearResourceForCallEnd");
			removeSipCallSession(callIdStr);
		}

		@Override
		public int sendFloorMsg(final String callIdStr,int type) throws RemoteException
		{
			Log.d(THIS_FILE, "sendFloorMsg callIdStr is " + callIdStr + " type = " + type);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			Log.d(THIS_FILE, "sendFloorMsg");
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			Log.d(THIS_FILE, "sendFloorMsg checkResult = " + checkResult);
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						Log.d(THIS_FILE, "sendFloorMsg 1");
						if(app.getCurAcc() != null
								&& app.getCurAcc().getMyCall(callIdStr) != null) {
							app.getCurAcc().getMyCall(callIdStr).sendFloorMsg(type);
						}
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;//(Integer) pjService.floorRequest(callId);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}


		@Override
		public int sendTransMsg(final String callIdStr,int type,String userId, long recverAudioSsrc, long recverVideoSsrc) throws RemoteException
		{
			Log.d(THIS_FILE, "sendTransMsg callIdStr is " + callIdStr  + " type = " + type + " userId = " + userId + " recverAudioSsrc = " + recverAudioSsrc + " recverVideoSsrc = " + recverVideoSsrc);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			Log.d(THIS_FILE, "sendTransMsg");
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).sendTransMsg(type,userId,recverAudioSsrc,recverVideoSsrc);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;//(Integer) pjService.floorRelease(callId);
//					return 0;//(Integer) pjService.transRequest(callId);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}
		@Override
		public int sendRecvMsg(final String callIdStr,final int type,final String userId,final long recverAudioSsrc,final long recverVideoSsrc) throws RemoteException
		{
			Log.d(THIS_FILE, "sendRecvMsg callIdStr is " + callIdStr  + " type = " + type + " userId = " + userId + " recverAudioSsrc = " + recverAudioSsrc + " recverVideoSsrc = " + recverVideoSsrc);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).sendRecvMsg(type,userId,recverAudioSsrc,recverVideoSsrc);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;//(Integer) pjService.floorRelease(callId);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}


//		@Override
//		public int negotiateRtpChannel(final String callIdStr) throws RemoteException
//		{
//			Log.d(THIS_FILE, "negotiateRtpChannel callIdStr is " + callIdStr );
////			SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			Log.d(THIS_FILE, "negotiateRtpChannel");
////			final MyAccount acc = getAccount();
//			int checkResult = app.checkSystemErr();
//			if(checkResult != CauseCont.OperateCheck_Suc)
//			{
//				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
//				notifyUserOfMessage(errStr);
//				return checkResult;
//			}
//
//			ReturnRunnable action = new ReturnRunnable()
//			{
//				@Override
//				protected Object runWithReturn() throws SameThreadException
//				{
//					try {
////						app.getCurAcc().getMyCall(callIdStr).negoRtp();
//					} catch (Exception e) {
//						e.printStackTrace();
//						return CauseCont.OperateErr_Stack;
//					}
//					return CauseCont.OperateCheck_Suc;//(Integer) pjService.negotiateRtpChannel(callId);
//				}
//			};
//			getExecutor().execute(action);
//			return SipManager.SUCCESS;
//		}
//
//		@Override
//		public int negotiateVideoChannel(final String callIdStr) throws RemoteException
//		{
//			Log.d(THIS_FILE, "negotiateVideoChannel callIdStr is " + callIdStr );
////			SipService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
////			final MyAccount acc = getAccount();
//			int checkResult = app.checkSystemErr();
//			if(checkResult != CauseCont.OperateCheck_Suc)
//			{
//				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
//				notifyUserOfMessage(errStr);
//				return checkResult;
//			}
//
//			ReturnRunnable action = new ReturnRunnable()
//			{
//				@Override
//				protected Object runWithReturn() throws SameThreadException
//				{
//					try {
////						app.getCurAcc().getMyCall(callIdStr).negoVideoRtp();
//					} catch (Exception e) {
//						e.printStackTrace();
//						return CauseCont.OperateErr_Stack;
//					}
//					return CauseCont.OperateCheck_Suc;//(Integer) pjService.negotiateVideoChannel(callId);
//				}
//			};
//			getExecutor().execute(action);
//			return SipManager.SUCCESS;
//		}

		@Override
		public void requestAudioFocus(boolean isSetSnd) throws RemoteException
		{
			Log.d(THIS_FILE, "requestAudioFocus");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			pjService.requestAudioFocus(isSetSnd);
		}

		@Override
		public void releaseAudioFocus() throws RemoteException
		{
			Log.d(THIS_FILE, "requestAudioFocus");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			pjService.releaseAudioFocus();
		}

		@Override
		public void onVol(int type) throws RemoteException
		{
			Log.d(THIS_FILE, "onVol type = " + type);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			if(app != null
					&& app.getAudioManager() != null)
			{
				app.getAudioManager().onVol(type);
			}
//			pjService.onVol(type);
		}

		@Override
		public boolean setSpeakerphoneEnable(boolean enable) throws RemoteException
		{
			Log.d(THIS_FILE, "setSpeakerphoneEnable on = " + enable);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			if(app != null
					&& app.getAudioManager() != null)
			{
				return app.getAudioManager().setSpeakerphoneEnable(enable);
			}
			return enable;
		}

		@Override
		public boolean setMicrophoneEnable(boolean enable) throws RemoteException
		{
			Log.d(THIS_FILE, "setMicrophoneEnable enable = " + enable);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			if(app != null
					&& app.getAudioManager() != null)
			{
				return app.getAudioManager().setMicrophoneEnable(enable);
			}
			return enable;
		}

		@Override
		public int opAudioMedia(final String callIdStr,final int opType) throws RemoteException
		{
			Log.d(THIS_FILE, "opAudioMedia callIdStr = " + callIdStr + " opType = " + opType);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).opAudioMedia(opType);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int opVideoMedia(final String callIdStr,final int opType) throws RemoteException
		{
			Log.d(THIS_FILE, "opVideoMedia callIdStr = " + callIdStr + " opType = " + opType);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).opVideoMedia(opType);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int opVideoStream(final String callIdStr,final int opType) throws RemoteException
		{
			Log.d(THIS_FILE, "opVideoStream callIdStr = " + callIdStr + " opType = " + opType);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).opVideoStream(opType);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int switchCamera(final String callIdStr) throws RemoteException
		{
			Log.d(THIS_FILE, "switchCamera callIdStr = " + callIdStr);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).switchCamera();
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int sendKeyFrame(final String callIdStr) throws RemoteException
		{
			Log.d(THIS_FILE, "sendKeyFrame callIdStr = " + callIdStr);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).sendKeyFrame();
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public int opVideoWinShow(final String callIdStr,final boolean isLocal,boolean isShow) throws RemoteException
		{
			Log.d(THIS_FILE, "opVideoWinShow callIdStr = " + callIdStr + " isLocal = " + isLocal + " isShow = " + isShow);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					try {
						app.getCurAcc().getMyCall(callIdStr).opVideoWinShow(isLocal,isShow);
					} catch (Exception e) {
						e.printStackTrace();
						return CauseCont.OperateErr_Stack;
					}
					return CauseCont.OperateCheck_Suc;
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		@Override
		public void ring(int type,boolean isRing) throws RemoteException
		{
			Log.d(THIS_FILE, "ring isRing = " + isRing);
			if(app != null
					&& app.getAudioManager() != null)
			{
				if(isRing)
				{
					if(type == 1) {
						app.getAudioManager().startRing();
					}else if(type == 2) {
						app.getAudioManager().startNotificationtone();
					}
				}
				else
				{
					app.getAudioManager().stopRing();
				}
			}
		}
		@Override
		public void updateAudioChannel(int callType,boolean hasVideo)
		{
			if(app != null
					&& app.getAudioManager() != null)
			{
				app.getAudioManager().updateAudioChannel(callType,hasVideo);
			}
		}
		/**
		 * {@inheritDoc}
		 */
		@Override
		public int reinvite(final int callId, final int unhold,final String extStr,final String negoStr) throws RemoteException
		{
			Log.d(THIS_FILE, "reinvite callId is " + callId + " unhold is " + unhold + " extStr is " + extStr + " negoStr is " + negoStr);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			Log.d(THIS_FILE, "REINVITING");
//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				String errStr = CauseCont.getOperateCheckErrStr(checkResult,getApplicationContext());
				notifyUserOfMessage(errStr);
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable()
			{
				@Override
				protected Object runWithReturn() throws SameThreadException
				{
					return 0;//(Integer) pjService.callReinvite(acc.id,callId, unhold,extStr);
				}
			};
			getExecutor().execute(action);
			return SipManager.SUCCESS;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int hold(final int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "hold callId is " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);

//			final MyAccount acc = getAccount();
			int checkResult = app.checkSystemErr();
			if(checkResult != CauseCont.OperateCheck_Suc)
			{
				return checkResult;
			}

			ReturnRunnable action = new ReturnRunnable() {
				@Override
				protected Object runWithReturn() throws SameThreadException {
					return 0;//(Integer) pjService.callHold(callId);
				}
			};
			getExecutor().execute(action);
			return (Integer) action.getResult();
		}


		/**
		 * {@inheritDoc}
		 */
		@Override
		public SipCallSession[] getCalls() throws RemoteException
		{
			Log.d(THIS_FILE, "getCalls");
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if(pjService != null) {
//				SipCallSession[] listOfCallsImpl = pjService.getCalls();
//				SipCallSession[] result = new SipCallSession[listOfCallsImpl.length];
//				for(int sessIdx = 0; sessIdx < listOfCallsImpl.length; sessIdx++) {
//					result[sessIdx] = new SipCallSession(listOfCallsImpl[sessIdx]);
//				}
//				return result;
//			}
			return new SipCallSession[0];
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void confAdjustTxLevel(final int port, final float value) throws RemoteException
		{
			Log.d(THIS_FILE, "confAdjustTxLevel port = " + port + " value = " + value);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					if(pjService == null) {
//						return;
//					}
//					pjService.confAdjustTxLevel(port, value);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void confAdjustRxLevel(final int port, final float value) throws RemoteException
		{
			Log.d(THIS_FILE, "confAdjustRxLevel port = " + port + " value = " + value);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					if(pjService == null) {
//						return;
//					}
//					pjService.confAdjustRxLevel(port, value);
				}
			});

		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public long confGetRxTxLevel(final int port) throws RemoteException
		{
			Log.d(THIS_FILE, "confGetRxTxLevel port = " + port);
			ReturnRunnable action = new ReturnRunnable() {
				@Override
				protected Object runWithReturn() throws SameThreadException {

					return 0;//(Long) pjService.getRxTxLevel(port);
				}
			};
			getExecutor().execute(action);
			return (Long) action.getResult();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void setEchoCancellation(final boolean on) throws RemoteException
		{
			Log.d(THIS_FILE, "setEchoCancellation on = " + on);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if(pjService == null) {
//				return;
//			}
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.setEchoCancellation(on);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void startRecording(final int callId, final int way) throws RemoteException
		{
			Log.d(THIS_FILE, "startRecording callId = " + callId + " way = " + way);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if (pjService == null) {
//				return;
//			}
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.startRecording(callId, way);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void stopRecording(final int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "stopRecording callId = " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if (pjService == null) {
//				return;
//			}
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.stopRecording(callId);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean isRecording(int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "isRecording callId = " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if(pjService == null) {
//				return false;
//			}
//			SipCallSession info = pjService.getCallInfo(callId);
//			if(info != null) {
//				return info.isRecording();
//			}
			return false;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean canRecord(int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "canRecord callId = " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if(pjService == null) {
			return false;
//			}
//			return pjService.canRecord(callId);
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void startVol(final int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "startVol callId = " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if (pjService == null) {
//				return;
//			}
//			isOtherUsed = false;
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.startVol(callId);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void stopVol(final int callId) throws RemoteException
		{
			Log.d(THIS_FILE, "stopVol callId = " + callId);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if (pjService == null) {
//				return;
//			}
//			isOtherUsed = true;
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.stopVol(callId);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void playWaveFile(final String filePath, final int callId, final int way) throws RemoteException
		{
			Log.d(THIS_FILE, "playWaveFile filePath = " + filePath + " callId = " + callId + " way = " + way);
			PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//			if(pjService == null) {
//				return;
//			}
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//					pjService.playWaveFile(filePath, callId, way);
				}
			});
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public int getVersion() throws RemoteException
		{
			return SipManager.CURRENT_API;
		}


		@Override
		public boolean isActiveCall() throws RemoteException
		{
//			SipCallSession activeCall = pjService.getActiveCallInProgress();
//			if(activeCall == null)
//			{
//				return false;
//			}
			return true;
		}
		@Override
		public boolean isIncommingCall() throws RemoteException
		{
//			SipCallSession activeCall = pjService.getIncommingCallInProgress();
//			if(activeCall == null)
//			{
//				return false;
//			}
			return true;
		}

		@Override
		public int updateCallOptions(int callId,boolean isChangeCamera,boolean isHaveVideo)
				throws RemoteException {
			// TODO Auto-generated method stub
//			if( pjService != null )
//			{
//				return pjService.updateCallOptions(callId, isChangeCamera,isHaveVideo);
//			}

			return -1;
		}
		@Override
		public String showCallInfosDialog(final int callId) throws RemoteException {
			ReturnRunnable action = new ReturnRunnable() {
				@Override
				protected Object runWithReturn() throws SameThreadException {
					String infos = "";//PjSipCalls.dumpCallInfo(callId);
					Log.d(THIS_FILE, infos);
					return infos;
				}
			};

			getExecutor().execute(action);
			return (String) action.getResult();
		}
		public String getLocalNatType() throws RemoteException {
			ReturnRunnable action = new ReturnRunnable() {
				@Override
				protected Object runWithReturn() throws SameThreadException {
					return "";//(String) pjService.getDetectedNatType();
				}
			};
			getExecutor().execute(action);
			return (String) action.getResult();
		}

		@Override
		public void setSnd( ) throws RemoteException {
			Log.d(THIS_FILE, "setSnd");
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//							if( pjService != null ){
//								pjService.setSnd( );
//							}
				}
			});
		}
		@Override
		public void setNoSnd( ) throws RemoteException {
			Log.d(THIS_FILE, "setNoSnd");
			getExecutor().execute(new SipRunnable() {
				@Override
				protected void doRun() throws SameThreadException {
//							if( pjService != null ){
//								pjService.setNoSnd();
//							}
				}
			});
		}
	};

	private SipServiceExecutor mExecutor;
	private static HandlerThread executorThread;


	public SipServiceExecutor getExecutor() {
		// create mExecutor lazily
		if (mExecutor == null) {
			mExecutor = new SipServiceExecutor(this);
		}
		return mExecutor;
	}

	protected void addAccount(ImsProfile imsProfile,String pwd)
	{
//		String domain = svrIp + ":" + port;
//		String idUrl = "sip:" + num + "@" + domain;
		Log.d(THIS_FILE, "addAccount imsProfile = " + imsProfile);

		try {
			boolean suc = app.addCurAcc(imsProfile,pwd);
			Log.d(THIS_FILE, "addAccount suc = " + suc);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}


//	public void cleanStop () {
//		getExecutor().execute(new DestroyRunnable());
//	}

	//private KeepAliveTimer kaAlarm;
	// This is always done in SipExecutor thread
	private void startSipStack() throws SameThreadException
	{
		Log.d(THIS_FILE, "startSipStack app = " + app);

		if (app == null) {
			app = new MyApp(this);
			// Wait for GDB to init, for native debugging only
//			if (false &&
//					(getApplicationInfo().flags &
//							ApplicationInfo.FLAG_DEBUGGABLE) != 0)
//			{
//				try {
//					Thread.sleep(5000);
//				} catch (InterruptedException e) {}
//			}

//			app.init();
		}
		this.onBroadcastSipStackState(SipManager.SIPSTACK_START);
		Log.d(THIS_FILE, "Ask pjservice to start itself");
	}

	private void onBroadcastSipStackState(final int status) {
		Log.d(THIS_FILE, "onBroadcastSipStackState status = " + status);
		Intent intent = new Intent(
				SipManager.ACTION_SIP_STACK_CHANGED);
		intent.setPackage("com.cloudptt.wp");
		intent.putExtra(SipManager.SIPSTACK_STATUS, status);

		sendBroadcast(intent,SipManager.PERMISSION_USE_SIP);
		Log.d(THIS_FILE, "onBroadcastSipStackState end");

	}


	/**
	 * Safe stop the sip stack
	 * @return true if can be stopped, false if there is a pending call and the sip service should not be stopped
	 */
//	public boolean stopSipStack() throws SameThreadException {
//		Log.d(THIS_FILE, "stopSipStack");
//		boolean canStop = true;
//		app.deinit();
//		return canStop;
//	}

	public void removeAllSipCallSession()
	{
		Log.d(THIS_FILE, "removeAllSipCallSession");
	}

	public void removeSipCallSession(String callIdStr)
	{
		Log.d(THIS_FILE, "removeSipCallSession callIdStr = " + callIdStr);
	}


	/**
	 * Notify user from a message the sip stack wants to transmit.
	 * For now it shows a toaster
	 * @param msg String message to display
	 */
	public void notifyUserOfMessage(String msg) {
		serviceHandler.sendMessage(serviceHandler.obtainMessage(TOAST_MESSAGE, msg));
	}


	/**
	 * Remove accounts from database
	 */
	private void unregisterAllAccounts(boolean cancelNotification) throws SameThreadException {

		Log.d(THIS_FILE, "unregisterAllAccounts");
//		releaseResources();
//		MyAccount acc = getAccount();
//		int checkResult = app.checkSystemErr();
//		if (checkResult != CauseCont.OperateCheck_Suc) {
//			String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//			notifyUserOfMessage(errStr);
//			return;
//		}
//		delCurAcc();
		try {
			app.getCurAcc().setRegistration(false);
		}catch (Exception e)
		{
			e.printStackTrace();
			Log.d(THIS_FILE, "unregisterAllAccounts e = " + e);
		}
	}
	private void refreshCurAcc() throws SameThreadException {

		Log.d(THIS_FILE, "refreshCurAcc");
//		int checkResult = app.checkSystemErr();
//		if (checkResult != CauseCont.OperateCheck_Suc) {
//			String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//			notifyUserOfMessage(errStr);
//			return;
//		}
		try {
			app.getCurAcc().setRegistration(true);
		}catch (Exception e)
		{
			e.printStackTrace();
			Log.d(THIS_FILE, "refreshCurAcc e = " + e);
		}
	}

	private void delCurAcc() throws SameThreadException {

		Log.d(THIS_FILE, "delCurAccount");
//		int checkResult = app.checkSystemErr();
//		if (checkResult != CauseCont.OperateCheck_Suc) {
//			String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//			notifyUserOfMessage(errStr);
//			return;
//		}
		boolean suc = app.delCurAcc();
		Log.d(THIS_FILE, "delCurAcc suc = " + suc);
	}

	//Binders for media manager to sip stack
	/**
	 * Adjust tx software sound level
	 * @param speakVolume volume 0.0 - 1.0
	 */
	public void confAdjustTxLevel(float speakVolume) throws SameThreadException {
//		if(pjService != null) {
//			pjService.confAdjustTxLevel(0, speakVolume);
//		}
	}
	/**
	 * Adjust rx software sound level
	 * @param speakVolume volume 0.0 - 1.0
	 */
	public void confAdjustRxLevel(float speakVolume) throws SameThreadException {
//		if(pjService != null) {
//			pjService.confAdjustRxLevel(0, speakVolume);
//		}
	}

//    public static boolean isVidOk(String callIdStr){
//        Log.d( THIS_FILE, "isVidOk");
//        if(singlePjsuaService != null) {
//            return singlePjsuaService.checkVidOk(callIdStr);
//        }
//        return false;
//    }
//    public boolean checkVidOk(String callIdStr)
//    {
//		Log.d( THIS_FILE, "checkVidOk");
//        PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//        int checkResult = app.checkSystemErr();
//        if (checkResult != CauseCont.OperateCheck_Suc) {
//            String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//            notifyUserOfMessage(errStr);
//            return false;
//        }
//        ReturnRunnable action = new ReturnRunnable() {
//            @Override
//            protected Object runWithReturn() throws SameThreadException {
//                try {
//                    if (app.getCurAcc().getMyCall(callIdStr) != null)
//                    {
//                        return app.getCurAcc().getMyCall(callIdStr).isVidOk();
//                    }
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    return CauseCont.OperateErr_Stack;
//                }
//                return CauseCont.OperateCheck_Suc;//(Integer) pjService.negotiateVideoChannel(callId);
//            }
//        };
//        getExecutor().execute(action);
//        return (boolean) action.getResult();
//    }
//    public static int opStartPreView(String callIdStr,final Surface surface){
//        Log.d( THIS_FILE, "opStartPreView");
//        if(singlePjsuaService != null) {
//            return singlePjsuaService.startPreView(callIdStr,surface);
//        }
//        return CauseCont.OperateErr_Stack;
//    }
//    public int startPreView(String callIdStr,final Surface surface) {
//        Log.d(THIS_FILE, "startPreView");
//        PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
////            final MyAccount acc = getAccount();
//        int checkResult = app.checkSystemErr();
//        if (checkResult != CauseCont.OperateCheck_Suc) {
//            String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//            notifyUserOfMessage(errStr);
//            return checkResult;
//        }
//
//        ReturnRunnable action = new ReturnRunnable() {
//            @Override
//            protected Object runWithReturn() throws SameThreadException {
//                try {
//                    if (app.getCurAcc().getMyCall(callIdStr) != null) {
//						app.getCurAcc().getMyCall(callIdStr).startLocalSurfaceView(surface);
//                    }
//                }
//                catch (Exception e)
//                {
//                    e.printStackTrace();
//                }
//                return SipManager.SUCCESS;
//            }
//        };
//        getExecutor().execute(action);
//        return SipManager.SUCCESS;
//
////            if (account.getMyCall().vidPrev != null) {
////                account.getMyCall().startLocalSurfaceView(surface);
////            }
//    }
//    public static int opStopPreView(String callIdStr)
//    {
//        Log.d( THIS_FILE, "opStopPreView");
//        if(singlePjsuaService != null) {
//            return singlePjsuaService.stopPreView(callIdStr);
//        }
//        return CauseCont.OperateErr_Stack;
//    }
//    public int stopPreView(String callIdStr) {
//        Log.d(THIS_FILE, "stopPreView");
//        PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
////            final MyAccount acc = getAccount();
//        int checkResult = app.checkSystemErr();
//        if (checkResult != CauseCont.OperateCheck_Suc) {
//            String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
//            notifyUserOfMessage(errStr);
//            return checkResult;
//        }
//
//        ReturnRunnable action = new ReturnRunnable() {
//            @Override
//            protected Object runWithReturn() throws SameThreadException {
//                try {
//                    if (app.getCurAcc().getMyCall(callIdStr).vidPrev != null) {
//						app.getCurAcc().getMyCall(callIdStr).stopLocalSurfaceView();
//                    }
//                }
//                catch (Exception e)
//                {
//                    e.printStackTrace();
//                }
//                return SipManager.SUCCESS;
//            }
//        };
//        getExecutor().execute(action);
//        return SipManager.SUCCESS;
//    }
    public static int opSetSurfaceView(String callIdStr,SurfaceView surfaceInVideo, SurfaceView surfacePreview)
    {
        Log.d( THIS_FILE, "opSetSurfaceView");
        if(singlePjsuaService != null) {
            return singlePjsuaService.setSurfaceView(callIdStr,surfaceInVideo,surfacePreview);
        }
        return CauseCont.OperateErr_Stack;
    }
    public int setSurfaceView(String callIdStr, SurfaceView surfaceInVideo, SurfaceView surfacePreview) {
        Log.d(THIS_FILE, "setSurfaceView");
        PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//            final MyAccount acc = getAccount();
        int checkResult = app.checkSystemErr();
        if (checkResult != CauseCont.OperateCheck_Suc) {
            String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
            notifyUserOfMessage(errStr);
            return checkResult;
        }

        ReturnRunnable action = new ReturnRunnable() {
            @Override
            protected Object runWithReturn() throws SameThreadException {
                try {
                    if (app.getCurAcc().getMyCall(callIdStr) != null) {
						app.getCurAcc().getMyCall(callIdStr).setSufaceView(surfaceInVideo,surfacePreview);
                    }
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                return SipManager.SUCCESS;
            }
        };
        getExecutor().execute(action);
        return SipManager.SUCCESS;
//            if (account.getMyCall().vidWin != null) {
//                Log.d(TAG,"setRemoteSurfaceView");
//                account.getMyCall().setRemoSurfaceView(surface);
//            }
    }

	public static int opSetCaptureOrient(int orient)
	{
		Log.d( THIS_FILE, "opSetCaptureOrient");
		if(singlePjsuaService != null) {
			return singlePjsuaService.setCaptureOrient(orient);
		}
		return CauseCont.OperateErr_Stack;
	}
	public int setCaptureOrient(final int orient) {
		Log.d(THIS_FILE, "setCaptureOrient orient = " + orient);
		PjsuaService.this.enforceCallingOrSelfPermission(SipManager.PERMISSION_USE_SIP, null);
//            final MyAccount acc = getAccount();
		int checkResult = app.checkSystemErr();
		if (checkResult != CauseCont.OperateCheck_Suc) {
			String errStr = CauseCont.getOperateCheckErrStr(checkResult, getApplicationContext());
			notifyUserOfMessage(errStr);
			return checkResult;
		}

		ReturnRunnable action = new ReturnRunnable() {
			@Override
			protected Object runWithReturn() throws SameThreadException {
				if (app != null) {
					app.setCaptureOrient(orient);
				}
				return SipManager.SUCCESS;
			}
		};
		getExecutor().execute(action);
		return SipManager.SUCCESS;
		//        public void setCaptureOrient(int orient){
//            if (MyApp.ep != null && account != null) {
//                try {
//                    AccountConfig cfg = account.accCfg;//.cfg;
//                    int cap_dev = cfg.getVideoConfig().getDefaultCaptureDevice();
//                    MyApp.ep.vidDevManager().setCaptureOrient(cap_dev, orient,
//                            true);
//                } catch (Exception e) {
//                    System.out.println(e);
//                }
//            }
//        }
	}
	private static final int TOAST_MESSAGE = 0;

	private Handler serviceHandler = new ServiceHandler(this);

	private static class ServiceHandler extends Handler {
		WeakReference<PjsuaService> s;
		public ServiceHandler(PjsuaService sipService) {
			s = new WeakReference<PjsuaService>(sipService);
		}

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			PjsuaService sipService = s.get();
			if(sipService == null) {
				return;
			}
			if (msg.what == TOAST_MESSAGE) {
				Log.e(THIS_FILE, "msg=" + (msg.arg1!=0? msg.arg1: msg.obj) );
				if (msg.arg1 != 0) {
					Toast.makeText(sipService, msg.arg1, Toast.LENGTH_LONG).show();
				} else {
					Toast.makeText(sipService, (String) msg.obj, Toast.LENGTH_LONG).show();
				}
			}
		}
	};


	public static PjsuaService getSinglePjsuaService() {
		return singlePjsuaService;
	}
	private static Looper createLooper() {
		//	synchronized (executorThread) {
		if(executorThread == null) {
			Log.d(THIS_FILE, "Creating new handler thread");
			// ADT gives a fake warning due to bad parse rule.
			executorThread = new HandlerThread("SipService.Executor");
			executorThread.start();
		}
		//	}
		return executorThread.getLooper();
	}
	// Executes immediate tasks in a single executorThread.
	// Hold/release wake lock for running tasks
	public static class SipServiceExecutor extends Handler
	{
		WeakReference<PjsuaService> handlerService;

		SipServiceExecutor(PjsuaService s) {
			super(createLooper());
			handlerService = new WeakReference<PjsuaService>(s);
		}

		public void execute(Runnable task)
		{
			PjsuaService s = handlerService.get();
			if(s==null || null==s.sipWakeLock )
			{
				Log.w(THIS_FILE, "can't handle msg: s is null ");
			}
			if(s != null) {
				s.sipWakeLock.acquire(task);
			}
			else
			{
				Log.w(THIS_FILE, "execute handlerService.get() is null");
			}
			Message.obtain(this, 0/* don't care */, task).sendToTarget();
		}

		@Override
		public void handleMessage(Message msg)
		{
			if (msg.obj instanceof Runnable)
			{
				executeInternal((Runnable) msg.obj);
			}
			else
			{
				Log.w(THIS_FILE, "can't handle msg: " + msg);
			}
		}

		private void executeInternal(Runnable task)
		{
			try
			{
				Log.d(THIS_FILE, "begin run task: " + task.getClass());
				task.run();
			}
			catch (Throwable t)
			{
				Log.e(THIS_FILE, "run task: " + task.getClass(), t);
				t.printStackTrace();
			}
			finally
			{
				PjsuaService s = handlerService.get();
				if(s != null)
				{
					s.sipWakeLock.release(task);
				}
			}
		}
	}


	class StartRunnable extends SipRunnable {
		@Override
		protected void doRun() throws SameThreadException {
			Log.d(THIS_FILE, "StartRunnable");
			startSipStack();
		}
	}


//	class SyncStartRunnable extends ReturnRunnable {
//		@Override
//		protected Object runWithReturn() throws SameThreadException
//		{
//			Log.d(THIS_FILE, "SyncStartRunnable");
//			startSipStack();
//			return null;
//		}
//	}

	class StopRunnable extends SipRunnable {
		@Override
		protected void doRun() throws SameThreadException {
			Log.d(THIS_FILE, "StopRunnable");
			if(app != null)
			{
				app.deinit();
			}
		}
	}


	class RestartRunnable extends SipRunnable {
		@Override
		protected void doRun() throws SameThreadException
		{
			Log.d(THIS_FILE, "RestartRunnable");
//			if(app != null)
//			{
//				app.deinit();
//				app = null;
//			}
//			onBroadcastSipStackState(SipManager.SIPSTACK_STOP);
			if(app != null)
			{
				app.handleNetworkChange();
			}
			onBroadcastSipStackState(SipManager.SIPSTACK_START);
//			onBroadcastSipStackState(SipManager.SIPSTACK_START);
		}
	}

	class DestroyRunnable extends SipRunnable
	{
		@Override
		protected void doRun() throws SameThreadException
		{
			Log.d(THIS_FILE, "DestroyRunnable");
			if(app != null)
			{
				app.deinit();
				app = null;
			}

			onBroadcastSipStackState(SipManager.SIPSTACK_STOP);

			stopSelf();
		}
	}

	class FinalizeDestroyRunnable extends SipRunnable {
		@Override
		protected void doRun() throws SameThreadException
		{
			Log.d(THIS_FILE, "FinalizeDestroyRunnable");
			mExecutor = null;
			sipWakeLock.reset();

//			if(stopSipStack())
//			{
////				notificationManager.cancelAll();
////				notificationManager.cancelCalls();
//			}else {
//				Log.e(THIS_FILE, "Somebody has stopped the service while there is an ongoing call !!!");
//			}
			/* If we activate that we can get two concurrent executorThread 
			synchronized (executorThread) {
				HandlerThread currentHandlerThread = executorThread;
				executorThread = null;
				System.gc();
				// This is a little bit crappy, we are cutting were we sit.
				Threading.stopHandlerThread(currentHandlerThread, false);
			}
			 */
			if(app != null)
			{
				app.deinit();
				app = null;
			}
			// We will not go longer
			Log.i(THIS_FILE, "--- SIP SERVICE DESTROYED ---");
		}
	}

	// Enforce same thread contract to ensure we do not call from somewhere else
	public class SameThreadException extends Exception {
		private static final long serialVersionUID = -905639124232613768L;

		public SameThreadException() {
			super("Should be launched from a single worker thread");
		}
	}

	public abstract static class SipRunnable  implements Runnable {
		protected abstract void doRun() throws SameThreadException;

		public void run() {
			try {
				doRun();
			}catch(SameThreadException e) {
				Log.e(THIS_FILE, "Not done from same thread");
			}
		}
	}


	public abstract class ReturnRunnable extends SipRunnable {
		private Semaphore runSemaphore;
		private Object resultObject;

		public ReturnRunnable() {
			super();
			runSemaphore = new Semaphore(0);
			Log.d( THIS_FILE, "GET runSemaphore" );
		}

		public Object getResult() {
			try {
				runSemaphore.acquire();
			} catch (InterruptedException e) {
				Log.e(THIS_FILE, "Can't acquire run semaphore... problem...");
			}
			return resultObject;
		}

		protected abstract Object runWithReturn() throws SameThreadException;

		@Override
		public void doRun() throws SameThreadException {
			setResult(runWithReturn());
		}

		private void setResult(Object obj) {
			resultObject = obj;
			runSemaphore.release();
			Log.d( THIS_FILE, "RELEASE runSemaphore" );
		}
	}





//	public MyAccount getAccount()
//	{
//		return account;
//	}

//	public String rewriteNumber(String num)
//	{
//		String sipNum = reg_uri.replace("sip:", num + "@");
//		sipNum = "<sip:" + sipNum + ">";
//		Log.d(THIS_FILE, "rewriteNumber reg_uri = " + reg_uri + " num = " + num + " sipNum = " + sipNum);
//		return sipNum;
//	}


}
